package utils.binary.io;

import org.jetbrains.annotations.NotNull;
import utils.binary.impl.BytesSlice;
import utils.binary.BytesUtils;

import java.io.IOException;
import java.io.InputStream;

/**
 * @author Zhou Yifan
 */
public class BytesInputStream extends InputStream {

    private byte[] bytes;

    private int position;

    private int tail;

    public BytesInputStream(byte[] bytes, int offset, int size) {
        this.bytes = bytes;
        this.position = offset;
        this.tail = offset + size;
    }

    /**
     * <p>当前读取位置</p>
     * <p>也是输入流的原始字节数组的位置</p>
     *
     * @return int 值
     */
    public int getPosition() {
        return position;
    }

    /**
     * 当前输入流的原始字节数组
     *
     * @return byte[]
     */
    public byte[] getOriginBytes() {
        return bytes;
    }

    /**
     * 检查是否超出范围
     *
     * @param offset 起始位置
     * @param length 长度
     */
    private void checkBoundary(int offset, int length) {
        // assert offset >= position && offset + length <= tail : "The accessing index is out of BytesInputStream's bounds!";
        if (offset < position || offset + length > tail) {
            throw new IndexOutOfBoundsException(
                    "The accessing index is out of BytesInputStream's bounds! --[offset="
                            + offset + ", length=" + length + "]"
            );
        }
    }

    /**
     * 返回剩余的字节数；
     *
     * @return int
     */
    public int getSize() {
        return tail - position;
    }

    /**
     * 读取接下来的一个字节并返回
     *
     * @return byte
     */
    public byte readByte() {
        int off = this.position;
        checkBoundary(off, 1);
        position++;
        return bytes[off];
    }

    /**
     * 读取接下来的 2 个字节并返回16位字符；
     *
     * @return char
     */
    public char readChar() {
        int off = position;
        checkBoundary(off, 2);
        char ch = BytesUtils.toChar(bytes, off);
        position += 2;
        return ch;
    }

    /**
     * 读取接下来的 2 个字节并返回16位整数；
     *
     * @return short
     */
    public short readShort() {
        int off = position;
        checkBoundary(off, 2);
        short n = BytesUtils.toShort(bytes, off);
        position += 2;
        return n;
    }

    /**
     * 读取接下来的 4 个字节并返回32位整数；
     *
     * @return int
     */
    public int readInt() {
        int off = position;
        checkBoundary(off, 4);
        int n = BytesUtils.toInt(bytes, off);
        position += 4;
        return n;
    }

    /**
     * 读取接下来的 8 个字节并返回64位整数；
     *
     * @return long
     */
    public long readLong() {
        int off = position;
        checkBoundary(off, 8);
        long n = BytesUtils.toLong(bytes, off);
        position += 8;
        return n;
    }

    /**
     * 读取指定数量的字节并返回对应的字符串；
     *
     * @param size size
     * @return String
     */
    public String readString(int size) {
        int off = position;
        checkBoundary(off, size);
        String s = BytesUtils.toString(bytes, off, size);
        position += size;
        return s;
    }

    /**
     * 读取指定数量的字节并返回对应的字符串；
     *
     * @param size    size
     * @param charset charset
     * @return String
     */
    public String readString(int size, String charset) {
        int off = position;
        checkBoundary(off, size);
        String s = BytesUtils.toString(bytes, off, size, charset);
        position += size;
        return s;
    }

    /**
     * 读取指定数量的字节并返回
     *
     * @param size size
     * @return byte[]
     */
    public byte[] readBytes(int size) {
        byte[] copy = new byte[size];
        readBytes(copy, 0, size);
        return copy;
    }

    /**
     * 读取指定数量的字节并返回;
     *
     * @param buffer 指定缓存
     * @param offset 起始位置
     * @param size   读取大小
     * @return int 值，读取字节数
     */
    public int readBytes(byte[] buffer, int offset, int size) {
        int off = position;
        int s = tail - position;
        s = Math.min(s, size);
        checkBoundary(off, s);
        System.arraycopy(bytes, off, buffer, offset, s);
        position += s;
        return s;
    }

    /**
     * <p>从当前位置开始读取, 返回指定数量的字节片段</p>
     * <p>注：此操作将数据游标位置向后移动指定数量的字节</p>
     *
     * @param size size
     * @return {@link BytesSlice}
     */
    public BytesSlice readSlice(int size) {
        BytesSlice copy = getSlice(size);
        position += size;
        return copy;
    }

    /**
     * <p>从当前位置开始读取, 返回剩余字节的片段</p>
     * <p>注：此操作不影响游标位置</p>
     *
     * @return {@link BytesSlice}
     */
    public BytesSlice getSlice() {
        if (tail == position) {
            return BytesSlice.EMPTY;
        }
        return getSlice(tail - position);
    }

    /**
     * <p>从当前位置开始读取, 返回指定数量的字节片段</p>
     * <p>注：此操作不影响游标位置</p>
     *
     * @param size size
     * @return {@link BytesSlice}
     */
    public BytesSlice getSlice(int size) {
        int off = position;
        checkBoundary(off, size);
        return new BytesSlice(bytes, off, size);
    }

    /**
     * 跳过一些字节，实际上是改变游标位置
     *
     * @param size 需要跳过的个数
     */
    public void skip(int size) {
        checkBoundary(position, size);
        position += size;
    }

    /**
     * 注：遵循 JDK 接口要求，读取一个字节以 int 类型返回；
     */
    @Override
    public int read() throws IOException {
        return readByte() & 0xFF;
    }

    @Override
    public int read(byte @NotNull [] b, int off, int len) throws IOException {
        return readBytes(b, off, len);
    }

    @Override
    public int read(byte @NotNull [] b) throws IOException {
        return read(b, 0, b.length);
    }

    @Override
    public int available() throws IOException {
        return getSize();
    }

    @Override
    public long skip(long n) throws IOException {
        if (n < 0) {
            throw new IllegalArgumentException("Specified a negative number of bytes to be skipped!");
        }
        if (n >= Integer.MAX_VALUE) {
            throw new IllegalArgumentException("The number of bytes to be skipped is out of max value!");
        }
        skip((int) n);
        return n;
    }
}
